// ActivatorImpl.h
// 2008/12/18

#pragma once

#include "helper/UThreading.h"
#include "helper/TSFHack.h"
#include "helper/UException.h"

// ActivatorImpl
class ActivatorImpl :
	public LuoSnapShot::UActivator,
	public LuoUtils::URunnable {

	LuoUtils::Thread m_Thread;

	LuoUtils::CritSec m_CSec;

	LuoUtils::Event m_Event;

	HWND m_hTool;
	HWND m_hWeb;

public:

	ActivatorImpl() :
		m_hTool(0),
		m_hWeb(0)
	{
	}

	~ActivatorImpl()
	{
		SafeRelease();
	}

	/* */

	virtual HRESULT STDCALL Initialize()
	{
		HRESULT hRslt = m_Event.Create(true);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		try {
			m_Thread.SetupName("LuoSnapShot");
			m_Thread.Create(this);
		} catch (LuoUtils::Exception&) {
			return E_FAIL;
		}

		{
			DWORD result = WaitForSingleObject(
				m_Event,
				3000);
			if (result != WAIT_OBJECT_0) {
				return E_FAIL;
			}
		}

		// TestStartup();

		return S_OK;
	}

	virtual void STDCALL Uninitialize()
	{
		SafeRelease();
	}

	/* */

	void SafeRelease()
	{
		DWORD tid = m_Thread.GetThreadId();
		for (int i = 0; i < 3; i++) {
			if (i > 0) {
				::Sleep(100);
			}
			if (PostThreadMessage(tid, WM_QUIT, 0, 0)) {
				break;
			}
		}

		HANDLE hThread = m_Thread;
		if (hThread != 0) {
			HANDLE handles[] = {
				hThread
			};

			for (; ; ) {
				DWORD dwResult = ::MsgWaitForMultipleObjects(
					1,
					handles,
					FALSE,
					50,
					QS_ALLINPUT);
				if (dwResult == WAIT_OBJECT_0) {
					break;
				}

				MSG msg = { 0 };

				BOOL fPeek = ::PeekMessageW(
					&msg,
					0,
					0,
					0,
					PM_REMOVE);
				if (fPeek) {
					::TranslateMessage(&msg);
					::DispatchMessageW(&msg);
				}
			}
		}

		m_Thread.Join();
		m_Thread.Close();
	}

	/* */

	bool InitToolFrame()
	{
		{
			HWND hwnd = ToolFrame::CreateFrame();
			if (hwnd == 0) {
				return false;
			}

			m_hTool = hwnd;
		}

		{
			HWND hwnd = WebFrame::CreateFrame();
			if (hwnd == 0) {
				return false;
			}

			m_hWeb = hwnd;
		}

		return true;
	}

	void UninitToolFrame()
	{
		if (m_hTool != 0) {
			DestroyWindow(m_hTool);
			m_hTool = 0;
		}

		if (m_hWeb != 0) {
			DestroyWindow(m_hWeb);
			m_hWeb = 0;
		}
	}

	/* */

	virtual int STDCALL Run()
	{
		HRESULT hRslt = ::OleInitialize(0);
		if (FAILED(hRslt)) {
			return -1;
		}

		int result = -1;
		{
			LuoUtils::TSFManager tsf;
			tsf.Activate();

			result = RunImpl();

			tsf.Release();
		}

		::OleUninitialize();

		return result;
	}

	int RunImpl()
	{
		if (!InitToolFrame()) {
			return -1;
		}

		// Prepare Windows Message Queue
		{
			MSG msg = { 0 };
			BOOL fPeek = ::PeekMessageW(
				&msg,
				0,
				0,
				0,
				PM_NOREMOVE);
		}

		m_Event.SetEvent();

		int result = DoProcess();

		UninitToolFrame();

		return result;
	}

	int DoProcess()
	{
		int result = -1;

		for (; ; ) {
			MSG msg = { 0 };

			BOOL fPeek = ::PeekMessageW(
				&msg,
				0,
				0,
				0,
				PM_REMOVE);
			if (fPeek) {
				if (msg.message == WM_QUIT) {
					result = (int)msg.wParam;
					break;
				}

				if (!IsPreprocessWindowMessage(&msg)) {
					::TranslateMessage(&msg);
					::DispatchMessageW(&msg);
				}

			} else {
				WaitMessage();
			}
		}

		return result;
	}

	bool IsPreprocessWindowMessage(MSG* msg)
	{
		return false;
	}

	/* */

#if 0
	virtual HRESULT STDCALL CallSync(
		LuoSnapShot::CallParam* param)
	{
		if (param == 0) {
			return E_INVALIDARG;
		}

		if (m_hTool == 0) {
			return E_UNEXPECTED;
		}

		::SendMessage(
			m_hTool,
			WM_USERAP_CALLSYNC,
			0,
			(LPARAM)param);

		return S_OK;
	}

	virtual HRESULT STDCALL CallSyncTimeout(
		LuoSnapShot::CallParam* param,
		int                  timeout)
	{
		if (param == 0) {
			return E_INVALIDARG;
		}

		if (m_hTool == 0) {
			return E_UNEXPECTED;
		}

		DWORD_PTR result = 0;
		LRESULT code = ::SendMessageTimeout(
			m_hTool,
			WM_USERAP_CALLSYNC,
			0,
			(LPARAM)param,
			SMTO_NORMAL,
			UINT(timeout),
			&result);
		if (code == 0) {
			DWORD dwError = GetLastError();
			return HRESULT_FROM_WIN32(dwError);
		}

		return S_OK;
	}
#endif

	/* */

#if 0
	void TestStartup()
	{
		if (m_hWeb != 0) {
			WCHAR path[MAX_PATH];
			GetModuleFileNameW(0, path, MAX_PATH);
			PathRemoveFileSpecW(path);

			WCHAR image[MAX_PATH];
			PathCombineW(image, path, L"..\\kesalin-cpp.png");

			WebEntry item;

			item.Uri  = L"http://www.cppblog.com/kesalin";
			item.Path = image;

			item.ImageWidth  = 320;
			item.ImageHeight = 240;

			item.Target  = 0;
			item.Message = 0;

			item.Cookie = 0;

			item.PageWidth  = 1024;
			item.PageHeight =  768;

			item.ShotDelay       =  2 * 1000;
			item.NavigateTimeout = 20 * 1000;

			item.Show = true;

			SendMessage(m_hWeb, WM_ADD_WEBENTRY, 0, (LPARAM)&item);

			PathCombineW(image, path, L"..\\kesalin-csdn.png");

			item.Uri  = L"http://blog.csdn.net/kesalin";
			item.Path = image;

			SendMessage(m_hWeb, WM_ADD_WEBENTRY, 0, (LPARAM)&item);
		}
	}
#endif

	/* */

	virtual HRESULT STDCALL AddWebItem(
		const LuoSnapShot::WebItem* item)
	{
		if (item == 0) {
			return E_INVALIDARG;
		}

		if (m_hWeb == 0) {
			return E_UNEXPECTED;
		}

		WebEntry* p = new WebEntry();

		p->Uri             = item->Uri;
		p->Path            = item->Path;
		p->ImageWidth      = item->ImageWidth;
		p->ImageHeight     = item->ImageHeight;
		p->Target          = item->Target;
		p->Message         = item->Message;
		p->Cookie          = item->Cookie;
		p->PageWidth       = item->PageWidth;
		p->PageHeight      = item->PageHeight;
		p->ShotDelay       = item->ShotDelay;
		p->NavigateTimeout = item->NavigateTimeout;
		p->Show            = item->Show;

		if (!PostMessage(m_hWeb, WM_POST_WEBENTRY, 0, (LPARAM)p)) {
			HRESULT hRslt = HRESULT_FROM_WIN32(GetLastError());
			delete p;
			return hRslt;
		}

		return S_OK;
	}

	/* */

	virtual HRESULT STDCALL DumpClipboard(
		const LuoSnapShot::ClipDump* item)
	{
		if (item == 0) {
			return E_INVALIDARG;
		}

		Clipboard dump(item);

		HRESULT hRslt = dump.Dump();
		if (FAILED(hRslt)) {
			return hRslt;
		}

		return S_OK;
	}

	/* */

}; // ActivatorImpl

